#include "platform.h"
#include "../utils/utils.h"
#include "../main/benchmark.h"

#include <stdio.h>
#include <vector>
#include <process.h>
#include <winsock2.h>


#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#ifdef _MSC_VER
#define _CRT_SECURE_NO_DEPRECATE
#pragma warning(disable : 4996)
#endif

#ifndef STRICT
#define STRICT
#endif

// cel: windows 2000 (igazabol csak a GlobalMemoryStatusEx() miatt kell)

#undef _WIN32_WINDOWS
#undef WINVER
#undef _WIN32_WINNT

#ifndef _WIN32_WINNT
#define _WIN32_WINNT        0x0500
#endif

#ifndef _WIN32_WINDOWS
#define _WIN32_WINDOWS      0x0500
#endif
#ifndef WINVER
#define WINVER              0x0500
#endif

#define NOGDICAPMASKS     // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
#define NOVIRTUALKEYCODES // VK_*
#define NOWINMESSAGES     // WM_*, EM_*, LB_*, CB_*
#define NOWINSTYLES       // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
#define NOSYSMETRICS      // SM_*
#define NOMENUS           // MF_*
#define NOICONS           // IDI_*
#define NOKEYSTATES       // MK_*
#define NOSYSCOMMANDS     // SC_*
#define NORASTEROPS       // Binary and Tertiary raster ops
#define NOSHOWWINDOW      // SW_*
#define OEMRESOURCE       // OEM Resource values
#define NOATOM            // Atom Manager routines
#define NOCLIPBOARD       // Clipboard routines
#define NOCOLOR           // Screen colors
#define NOCTLMGR          // Control and Dialog routines
#define NODRAWTEXT        // DrawText() and DT_*
#define NOGDI             // All GDI defines and routines
#define NOKERNEL          // All KERNEL defines and routines
#define NOUSER            // All USER defines and routines
#define NONLS             // All NLS defines and routines
#define NOMB              // MB_* and MessageBox()
#define NOMEMMGR          // GMEM_*, LMEM_*, GHND, LHND, associated routines
#define NOMETAFILE        // typedef METAFILEPICT

#ifndef NOMINMAX          // a mingw bekavar kicsit...
#define NOMINMAX          // Macros min(a,b) and max(a,b)
#endif

#define NOMSG             // typedef MSG and associated routines
#define NOOPENFILE        // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
#define NOSCROLL          // SB_* and scrolling routines
#define NOSERVICE         // All Service Controller routines, SERVICE_ equates, etc.
#define NOSOUND           // Sound driver routines
#define NOTEXTMETRIC      // typedef TEXTMETRIC and associated routines
#define NOWH              // SetWindowsHook and WH_*
#define NOWINOFFSETS      // GWL_*, GCL_*, associated routines
#define NOCOMM            // COMM driver routines
#define NOKANJI           // Kanji support stuff.
#define NOHELP            // Help engine interface.
#define NOPROFILER        // Profiler interface.
#define NODEFERWINDOWPOS  // DeferWindowPos routines
#define NOMCX             // Modem Configuration Extensions

#include <windows.h>


static LARGE_INTEGER g_Freq;
static BOOL g_Usetimer = QueryPerformanceFrequency(&g_Freq);

double os_GetTime()
{
    if (g_Usetimer)
    {
        LARGE_INTEGER current;
        QueryPerformanceCounter(&current);
        return current.QuadPart / (double)g_Freq.QuadPart;
    }
    else
    {
        return GetTickCount() / 1000.0;     // ez olyan ratyi, hogy inkabb ne is lenne...
    }
}


uint64_t os_GetPhysicalMemory()
{    
    MEMORYSTATUSEX status;
    memset(&status, 0, sizeof(status));
    status.dwLength = sizeof(status);
    GlobalMemoryStatusEx(&status);
    return status.ullTotalPhys;
}



static BOOL IsWow64()
{
    // 32 bites processz 64 bites oprendszeren?

    typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
    LPFN_ISWOW64PROCESS fnIsWow64Process;

    BOOL bIsWow64 = FALSE;

    fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")),"IsWow64Process");

    if (fnIsWow64Process)
    {
        if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
            return FALSE;
    }
    return bIsWow64;
}


void os_GetOSName(CChString *out)
{
    out->Clear();

    DWORD dwVersion, dwMajorVersion, dwMinorVersion, dwBuild;

    dwVersion = GetVersion();

    dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
    dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));

    if (dwVersion < 0x80000000)
        dwBuild = (DWORD)(HIWORD(dwVersion));
    else                                      // Windows Me/98/95
        dwBuild =  0;

    char buff[256];
    sprintf(buff, "Microsoft Windows %u.%u.%u; WOW64: %s", (unsigned)dwMajorVersion, (unsigned)dwMinorVersion, (unsigned)dwBuild, IsWow64() ? "igen" : "nem");

    *out = buff;

    // a _WIN32 macro _mindig_ definialva van

#if defined(_WIN64)
    *out += "; x64";
#elif defined(_WIN32)
    *out += "; x86";
#endif
}

void os_GetCRuntime(CChString *out)
{
    out->Clear();

#ifdef _MSC_VER
#ifdef _MT
    *out = "msvcrt - multi threaded";
#else
    *out = "msvcrt - single threaded";
#endif

#ifdef _DEBUG
    *out += ", debug";
#else
    *out += ", release";
#endif

#ifdef _CPPLIB_VER
    *out += ft(", v%d", _CPPLIB_VER);
#endif

#endif

    *out += ft("; ptr: %d byte", (int)sizeof(void*));
}


void os_GetCompiler(CChString *out)
{
    out->Clear();

#ifdef _MSC_VER
    *out += ft("MSVC++ v%d.%d", (int)_MSC_VER / 100, (int)_MSC_VER % 100);

#ifdef _CPPRTTI
    *out += ", RTTI";
#else
    *out += ", no RTTI";
#endif

#ifdef __MSVC_RUNTIME_CHECKS
    *out += ", runtime checks";
#else
    *out += ", no runtime checks";
#endif

#ifdef _CPPUNWIND
    *out += ", EH.";    // exception handling
#else
    *out += ", no EH.";
#endif


#ifdef _M_IX86
    *out += ft(", x86 (%d)", _M_IX86);
#endif

#ifdef _M_IA64
    *out = ", IA64";
#endif

#ifdef _M_X64
    *out += ", x64";
#endif


#if defined(_MANAGED) || defined(_M_CEE)
    *out += ", managed code";
#else
    *out += ", native code";
#endif

#ifdef __cplusplus_cli
    *out += ", C++/CLI";
#endif

#endif //_MSC_VER

#ifdef __GNUC__

    *out += __VERSION__;

#ifdef __EXCEPTIONS
    *out += ", EH";
#else
    *out += ", no EH";
#endif

#ifdef __USING_SJLJ_EXCEPTIONS__
    *out += ", old SJLJ EH";
#endif

#ifdef __SSP__
    *out += ", stack protector";
#endif

#ifdef __SSP_ALL__
    *out += ", stack protector all";
#endif

#endif //__GNUC__
}


int os_GetCPUClockMhz()
{
    HKEY hKey;
    DWORD value = 0;
    DWORD size = sizeof(DWORD);
    DWORD type = REG_DWORD;

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\SYSTEM\\CentralProcessor\\0", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
        return 0;

    RegQueryValueExW(hKey, L"~MHz", NULL, &type, (BYTE*)&value, &size);
    RegCloseKey(hKey);
    return (int)value;
}


void os_GetHw(CChString *out)
{
    out->Clear();

    HKEY hKey;
    char name[256];
    DWORD size = sizeof(name);
    DWORD type = REG_DWORD;

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\SYSTEM\\CentralProcessor\\0", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
        return ;

    RegQueryValueExA(hKey, "ProcessorNameString", NULL, &type, (BYTE*)name, &size);
    RegCloseKey(hKey);

    char hw[1024];
    sprintf(hw, "%s; %dMb RAM; %dMHz", name, (int)(os_GetPhysicalMemory() / (1024*1024)), os_GetCPUClockMhz());
    *out = hw;
}

// 
// szalak
//

void *os_CreateThreadVariables(int count)
{
    HANDLE *ah = new HANDLE[count];

    for (int i=0; i<count; i++)
        ah[i] = INVALID_HANDLE_VALUE;

    return ah;
}


void os_DeleteThreadVariables(void *&thread_variables, int count)
{
    if (!thread_variables)
        return;

    HANDLE *&threads = (HANDLE*&)thread_variables;

    for (int i=0; i<count; i++)
    {
        if (threads[i] != INVALID_HANDLE_VALUE)
            CloseHandle(threads[i]);
    }

    delete[] threads;
    threads = NULL;
}                          


static unsigned int __stdcall StartThreadHelper(void *thread)         // a pthread void fuggvenyt akar hivni, ezert itt is azt kell...
{
    ((THREADFUNC)thread)(NULL);
    _endthreadex(0);
    return 0;
}


void os_StartThread(void *thread_variables, int i, THREADFUNC thread, void *thread_param)
{
    HANDLE h = (HANDLE)_beginthreadex(NULL, 0, StartThreadHelper, thread, 0, NULL);

    if (h == 0)
        printf("Nem sikerult egy szal letrehozasa (%d hibakod)\n", errno);
    else
        ((HANDLE*)thread_variables)[i] = h;
}


void os_JoinThread(void *thread_variables, int i)
{
    HANDLE &h = ((HANDLE*)thread_variables)[i];

    if (h != INVALID_HANDLE_VALUE)
        WaitForSingleObject(h, INFINITE);
}


void os_CloseThread(void *thread_variables, int i)
{
    HANDLE &h = ((HANDLE*)thread_variables)[i];

    if (h != INVALID_HANDLE_VALUE)
    {
        WaitForSingleObject(h, INFINITE);
        CloseHandle(h);
        h = INVALID_HANDLE_VALUE;
    }
}

//
// net
//

bool os_NetInit()
{     
    WSAData wsa;

    memset(&wsa, 0, sizeof(wsa));    
    
    if (WSAStartup(MAKEWORD(2, 2), &wsa) < 0)
    {                               
        printf("Hiba az inicializalaskor");
        return false;
    }
    
    return true;
}


void os_NetCleanup()
{
    WSACleanup();
}

